using System;
using Moq;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class SequenceRuleSpecification {
        [Test]
        public void ShouldReturnTermAndIncrementIndexWhenParsed () {
            var sequence = new SequenceRule (new KeywordRule ("a"), new KeywordRule ("b"), new KeywordRule ("c"));

            var source = "a b c";

            var result = sequence.Parse (source, 0, new ParseContext ());
            Assert.IsNotNull (result);
            Assert.AreEqual (5, result.Index);
        }

        [Test]
        public void ShouldReturnNullAndDontIncrementIndexWhenCannotParse () {
            var sequence = new SequenceRule (new KeywordRule ("a"), new KeywordRule ("b"), new KeywordRule ("c"));

            var source = "a b x";

            var result = sequence.Parse (source, 0, new ParseContext ());
            Assert.IsNull (result);
        }

        [Test]
        public void ShouldAddSubNodesToReturnedCompositeTermWhenParsed () {
            var nonTerminal = new SequenceRule (
                new NamedRule ("a", IdentifierProduction.CreateTerminal ()),
                new KeywordRule ("="),
                new NamedRule ("b", IdentifierProduction.CreateTerminal ()));

            var source = "x = y";

            var result = nonTerminal.Parse (source, 0, new ParseContext ());
            Assert.IsNotNull (result);
            Assert.AreEqual (5, result.Index);

            Assert.AreEqual (2, result.Captures.Count);
            result.AssertCaptureIs (0, "a", "x");
            result.AssertCaptureIs (1, "b", "y");
        }

        [Test]
        public void ShouldPassContextFromOneRuleToTheNext () {
            var id1 = IdentifierProduction.CreateTerminal ();
            Func<ParseResult, ParseResult> transform = r => {
                                                         r.Context = r.Context.Clone ();
                                                         r.Context.BracketDepth++;
                                                         return r;
                                                     };
            id1.ResultTransform = transform;
            var id2 = IdentifierProduction.CreateTerminal ();
            id2.ResultTransform = transform;
            var id3 = IdentifierProduction.CreateTerminal ();
            id3.ResultTransform = transform;

            var nonTerminal = new SequenceRule (
                new NamedRule ("a", id1),
                new NamedRule ("b", id2),
                new NamedRule ("c", id3));

            var source = "a b x";

            var result = nonTerminal.Parse (source, 0, new ParseContext ());
            Assert.IsNotNull (result);
            Assert.AreEqual (3, result.Context.BracketDepth);
        }

        private static void AssertInfixSequence (bool isInfix, IProduction production, params IRule [] rules) {
            Assert.That (new SequenceRule (rules).InfixInformation.IsInfixWith (production), Is.EqualTo (isInfix));
        }

        private static IRule CreateHasProductionRule (IProduction production) {
            var rule = new Mock<IRule> ();
            rule.SetupGet (r => r.HasProduction).Returns (true);
            rule.SetupGet (r => r.Production).Returns (production);
            return rule.Object;
        }
    }
}